﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Hourglass.Qrobot.Http;
using Hourglass.Qrobot.MessageProcesser;
using Hourglass.Qrobot.Models;
using Hourglass.Qrobot.Utils;

namespace Hourglass.Qrobot.States
{
    public sealed class LoginedState : QQState
    {
        private int _oneZeroThreeCounter = 0;

        private readonly List<IVoidProcesser> _voidProcessers;
        private readonly List<IReplyProcesser> _replyProcessers;
        private readonly List<IIntervalProcesser> _intervalProcessers;

        public LoginedState()
        {
            _voidProcessers = new List<IVoidProcesser>();
            _replyProcessers = new List<IReplyProcesser>();
            _intervalProcessers = new List<IIntervalProcesser>();
            InitSelfImplement();
        }

        public override void Handler(QQContext context)
        {
            IntervalProcess(context);
            
            while (true)
            {
                var response = DataProvider.PollMessage(context.ToPollRequest());
                if (response.Code == 0)
                {
                    string idOrName;
                    if(response.Result == null) continue;

                    var message = response.ConvertToView(context, out idOrName);
                    if (message is KickMessage || message is ErrorMessage)
                    {
                        context.State = new WattingForGetQRCodeState();
                        break;
                    }
                    Task.Run(() => OnMessage(idOrName, message, response.Result[0], context));
                }
                else if (response.Code == 116)
                {
                    context.VerfiedField.Ptwebqq = response.Ptwebqq;
                }
                else if (response.Code == 103)
                {
                    if (++_oneZeroThreeCounter > context.Config.OneZeroThreeTimes)
                    {
                        context.State = new WattingForGetQRCodeState();
                        break;
                    }
                }
                else
                {
                    context.State = new WattingForGetQRCodeState();
                    break;
                }
            }
        }

        private void OnMessage(string idOrName, QqMessage message, PollMessage response, QQContext context)
        {
            Parallel.ForEach(_voidProcessers, processer =>
            {
                if (!message.TypeEquals(processer.MonitorType)) return;
                if (processer.MonitorType != MonitorType.All && !processer.NumberOrName.Equals(idOrName)) return;
                try
                {
                    processer.Process(message);
                }
                catch (Exception)
                {
                    // todo: 记录日志
                }
            });
            Parallel.ForEach(_replyProcessers, processer =>
            {
                if (!message.TypeEquals(processer.MonitorType)) return;
                if (processer.MonitorType != MonitorType.All && !processer.NumberOrName.Equals(idOrName)) return;
                try
                {
                    var replys = processer.Process(message);
                    foreach (var reply in replys)
                    {
                        ProcessReply(reply.Target, message, response, context);
                        var para = new SendMessageRequest
                        {
                            Target = reply.Target,
                            Content = reply.Content,
                            Face = context.Qq.UserInfo.Face,
                            ClientId = context.Config.ClientId,
                            Psessionid = context.VerfiedField.Psessionid,
                            CookieContainer = context.CookieHolder
                        };
                        DataProvider.SendMessage(para);
                    }
                }
                catch (Exception)
                {
                    // todo: 记录日志
                }
            });
        }

        private void IntervalProcess(QQContext context)
        {
            foreach (var processer in _intervalProcessers)
            {
                Task.Run(() =>
                { 
                    while (true)
                    {
                        try
                        {
                            var wannaSends = processer.Process();
                            foreach (var wannaSend in wannaSends)
                            {
                                if (wannaSend.Target.TargetType == ReplyTargetType.Default) return;

                                ProcessReply(wannaSend.Target, null, null, context);
                                var para = new SendMessageRequest
                                {
                                    Target = wannaSend.Target,
                                    Content = wannaSend.Content,
                                    Face = context.Qq.UserInfo.Face,
                                    ClientId = context.Config.ClientId,
                                    Psessionid = context.VerfiedField.Psessionid,
                                    CookieContainer = context.CookieHolder
                                };
                                DataProvider.SendMessage(para);
                            }
                            Thread.Sleep(processer.Interval);
                        }
                        catch (Exception)
                        {
                            // todo: 记录日志
                        }
                    }
                });
            }
        }

        private void InitSelfImplement()
        {
            var tVoid = typeof (IVoidProcesser);
            var tReply = typeof (IReplyProcesser);
            var tInterval = typeof (IIntervalProcesser);
            var types = Assembly.GetExecutingAssembly().GetTypes().Where(t => !t.IsInterface);
            foreach (var type in types)
            {
                var flag = 0;
                if (tVoid.IsAssignableFrom(type)) flag |= 1;
                if (tReply.IsAssignableFrom(type)) flag |= 2;
                if (tInterval.IsAssignableFrom(type)) flag |= 4;
                if (flag != 0)
                {
                    var obj = Activator.CreateInstance(type);
                    if ((flag & 1) == 1) _voidProcessers.Add((IVoidProcesser) obj);
                    if ((flag & 2) == 2) _replyProcessers.Add((IReplyProcesser) obj);
                    if ((flag & 4) == 4) _intervalProcessers.Add((IIntervalProcesser) obj);
                }
            }
        }

        private void ProcessReply(ReplyTarget target, QqMessage message, PollMessage response, QQContext context)
        {
            switch (target.TargetType)
            {
                case ReplyTargetType.Personal:
                    var qq = long.Parse(target.NumberOrName);
                    var friend = context.Qq.FindFriendByQq(qq);
                    if (friend != null)
                    {
                        target.NumberOrName = friend.Id.ToString();
                    }
                    else if (message is GroupMessage)
                    {
                        var member = context.Qq.FindMemberFromDisscussByQq(qq, response.Value.FromUin);
                        target.NumberOrName = member.Id.ToString();
                    }
                    else if (message is DisscussMessage)
                    {
                        var member = context.Qq.FindMemberFromGroupByQq(qq, response.Value.FromUin);
                        target.NumberOrName = member.Id.ToString();
                    }
                    break;
                case ReplyTargetType.Group:
                    var group = context.Qq.FindGroupByNumber(long.Parse(target.NumberOrName));
                    target.NumberOrName = group.Code.ToString();
                    break;
                case ReplyTargetType.Disscuss:
                    var disscuss = context.Qq.FindDisscussByName(target.NumberOrName);
                    target.NumberOrName = disscuss.Id.ToString();
                    break;
                case ReplyTargetType.Default:
                    target.NumberOrName = response.Value.FromUin.ToString();
                    if (message is PersonalMessage) target.TargetType = ReplyTargetType.Personal;
                    else if (message is GroupMessage) target.TargetType = ReplyTargetType.Group;
                    else if (message is DisscussMessage) target.TargetType = ReplyTargetType.Disscuss;
                    else throw new ArgumentException("message type error", nameof(target));
                    break;
                default:
                    throw new ArgumentException("target type error", nameof(target));
            }
        }
    }
}
